﻿using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;

namespace Chromium_Updater
{
    public partial class DownloadForm : Form
    {
        private Thread thrDownload;
        private Stream strResponse;
        private Stream strLocal;
        private HttpWebRequest webRequest;
        private HttpWebResponse webResponse;
        private static int PercentProgress;
        private delegate void UpdateProgessCallback(Int64 BytesRead, Int64 TotalBytes);
        bool goPause = false;
        //
        string FilePath;
        string Url;
        //
        private delegate void EndDownloadDelegate(DownloadFinishedArgs.ErrorCode ErrCode);
        public event Finished_Handler Finished;
        public event Progress_Handler ProgressChanged;

        public DownloadForm()
        {
            InitializeComponent();
        }

        #region Form Events

        public void Start(string Url, string DownTo)
        {
            this.Url = Url;
            this.FilePath = DownTo;
            //
            if (Settings.HideDownloader)
            {
                this.Opacity = 0;
                this.ShowInTaskbar = false;
            }
            this.Show();
            //
            if (thrDownload != null && thrDownload.ThreadState == ThreadState.Running)
                MessageBox.Show("A download is already running. Please either the stop the current download or await for its completion before starting a new one.", "Download in progress", MessageBoxButtons.OK, MessageBoxIcon.Error);
            else
            {
                // Let the user know we are connecting to the server
                label3.Text = "Download Starting";
                // Create a new thread that calls the Download() method
                thrDownload = new Thread(new ParameterizedThreadStart(Download));
                // Start the thread, and thus call Download(); start downloading from the beginning (0)
                thrDownload.Start(0);
                //
                button1.Enabled = true;
                button2.Enabled = true;
                //
                label3.Text = "Downloading";
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (thrDownload != null)
            {
                if (button2.Text == "Pause")
                {
                    goPause = true;
                    button2.Text = "Resume";
                    //
                    webResponse.Close();
                    strResponse.Close();
                    strLocal.Close();
                    thrDownload.Abort();
                }
                else
                {
                    goPause = false;
                    button2.Text = "Pause";
                    long startPoint = 0;
                    //
                    if (File.Exists(FilePath))
                    {
                        startPoint = new FileInfo(FilePath).Length;
                    }
                    else
                    {
                        MessageBox.Show("The file you choosed to resume doesn't exist.", "Could not resume", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    //
                    label3.Text = "Download Resuming";
                    thrDownload = new Thread(new ParameterizedThreadStart(Download));
                    thrDownload.Start(startPoint);
                    button2.Enabled = true;
                }
            }
            else
            {
                MessageBox.Show("A download does not appear to be in progress.", "Could not pause", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void button1_Click(object sender, EventArgs e)
        {
            progressBar1.Value = 0;
            label3.Text = "Download Stopped";
            button2.Enabled = false;
            //
            EndDownload(DownloadFinishedArgs.ErrorCode.DownloadCanceled);
        }

        #endregion

        #region Download Code

        private void Download(object startPoint)
        {
            try
            {
                int startPointInt = Convert.ToInt32(startPoint);
                webRequest = (HttpWebRequest)WebRequest.Create(Url);
                webRequest.AddRange(startPointInt);
                webRequest.Credentials = CredentialCache.DefaultCredentials;
                webResponse = (HttpWebResponse)webRequest.GetResponse();
                Int64 fileSize = webResponse.ContentLength;
                strResponse = webResponse.GetResponseStream();
                //
                if (startPointInt == 0)
                    strLocal = new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.None);
                else
                    strLocal = new FileStream(FilePath, FileMode.Append, FileAccess.Write, FileShare.None);
                //
                int bytesSize = 0;
                byte[] downBuffer = new byte[2048];
                //
                while ((bytesSize = strResponse.Read(downBuffer, 0, downBuffer.Length)) > 0)
                {
                    strLocal.Write(downBuffer, 0, bytesSize);
                    this.Invoke(new UpdateProgessCallback(this.UpdateProgress), new object[] { strLocal.Length, fileSize + startPointInt });
                    //
                    if (goPause)
                        break;
                }
            }
            catch (WebException)
            {
                EndDownload(DownloadFinishedArgs.ErrorCode.RemoteFileNotFound);
            }
            finally
            {
                if (!goPause) { EndDownload(DownloadFinishedArgs.ErrorCode.None); }
                else
                {
                    if (strResponse != null) { strResponse.Close(); }
                    if (strLocal != null) { strLocal.Close(); }
                }
            }
        }

        private void EndDownload(DownloadFinishedArgs.ErrorCode error_code)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EndDownloadDelegate(EndDownload), new object[] { error_code });
                return;
            }
            //
            if (thrDownload != null) { thrDownload.Abort(); }
            if (strResponse != null) { strResponse.Close(); }
            if (strLocal != null) { strLocal.Close(); }
            if (webResponse != null) { webResponse.Close(); }
            //
            if (error_code == DownloadFinishedArgs.ErrorCode.DownloadCanceled)
                File.Delete(FilePath);
            //
            this.Finished(this, new DownloadFinishedArgs(error_code, FilePath));
            this.Close();
        }

        private void UpdateProgress(Int64 BytesRead, Int64 TotalBytes)
        {
            PercentProgress = Convert.ToInt32((BytesRead * 100) / TotalBytes);
            //
            progressBar1.Value = PercentProgress;
            label1.Text = PercentProgress + "%";
            label4.Text = BytesRead + " / " + TotalBytes;
            //
            this.ProgressChanged(this, new DownloadProgressArgs(PercentProgress));
        }

        #endregion
    }

    #region DownloadFinishedArgs

    public delegate void Finished_Handler(object sender, DownloadFinishedArgs e);

    public class DownloadFinishedArgs : EventArgs
    {
        ErrorCode err;
        string path;

        public DownloadFinishedArgs(ErrorCode e, string p)
        {
            this.err = e;
            this.path = p;
        }

        public ErrorCode Error
        {
            get { return err; }
        }

        public string FullFilePath
        {
            get { return path; }
        }

        public enum ErrorCode : int
        {
            None = 0,
            RemoteFileNotFound = 1,
            DownloadCanceled = 2
        }
    }

    #endregion

    #region DownloadProgressArgs

    public delegate void Progress_Handler(object sender, DownloadProgressArgs myArgs);

    public class DownloadProgressArgs : EventArgs
    {
        int done;

        public DownloadProgressArgs(int pd)
        {
            this.done = pd;
        }

        public int PercentDone
        {
            get { return done; }
        }
    }

    #endregion
}
